/*
 * $Id: Sum.java,v 1.2 2007-03-07 19:14:56 klose Exp $
 */
package de.fanuffel.generic.structures;

import de.fanuffel.generic.Maybe;
import de.fanuffel.generic.Variable;
import de.fanuffel.generic.functions.Function;

public class Sum<X, Y> {

  private final X x;
  private final Y y;

  public Maybe<X> left() {
    if (x == null) {
      return new Maybe.Nothing<X>();
    } else {
      return new Maybe.Just<X>(x);
    }
  }

  public Maybe<Y> right() {
    if (y == null) {
      return new Maybe.Nothing<Y>();
    } else {
      return new Maybe.Just<Y>(y);
    }
  }

  public boolean left(Variable<X> v) {
    if (x != null) {
      return v.match(new Variable<X>(x));
    }
    return false;
  }

  public boolean right(Variable<Y> v) {
    if (x != null) {
      return v.match(new Variable<Y>(y));
    }
    return false;
  }

  protected Sum(X x, Y y) {
    assert (x == null || y == null);
    this.x = x;
    this.y = y;
  }

  public static <X, Y> Sum<X, Y> inl(X x) {
    return new Sum<X, Y>(x, null);
  }

  public static <X, Y> Sum<X, Y> inr(Y y) {
    return new Sum<X, Y>(null, y);
  }

  public <C> C apply(Function<X, C> f, Function<Y, C> g) {
    if (x != null) {
      return f.eval(x);
    } else {
      return g.eval(y);
    }
  }

  public static <X, Y, C> Function<Sum<X, Y>, C> arrow(final Function<X, C> f,
      final Function<Y, C> g) {
    return new Function<Sum<X, Y>, C>() {
      public C eval(Sum<X, Y> argument) {
        return argument.apply(f, g);
      }
    };
  }
}
